<!--
=========================================================
* Argon Dashboard - v1.2.0
=========================================================
* Product Page: https://www.creative-tim.com/product/argon-dashboard


* Copyright  Creative Tim (http://www.creative-tim.com)
* Coded by www.creative-tim.com



=========================================================
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head th:replace="fragments/common :: head_and_imports"></head>

<body>
<!-- Sidenav -->
<nav th:replace="fragments/common :: sidnav(5)"></nav>

<!-- Main content -->
<div class="main-content" id="panel">
	<!-- Topnav -->
	<nav class="navbar navbar-top navbar-expand navbar-dark bg-primary border-bottom d-md-none">
		<div class="container-fluid">
			<div class="collapse navbar-collapse row justify-content-between" id="navbarSupportedContent">
				<div class="row col-auto justify-content-end align-items-center m-0">
					<div class="col-auto">
						<ul class="navbar-nav align-items-center">
							<li class="nav-item d-xl-none">
								<!-- Sidenav toggler -->
								<div class="pr-2 sidenav-toggler sidenav-toggler-dark" data-action="sidenav-pin"
								     data-target="#sidenav-main">
									<div class="sidenav-toggler-inner">
										<i class="sidenav-toggler-line"></i>
										<i class="sidenav-toggler-line"></i>
										<i class="sidenav-toggler-line"></i>
									</div>
								</div>
							</li>
						</ul>
					</div>
				</div>
			</div>
		</div>
	</nav>
	
	<!-- Header -->
	<div class="header bg-primary">
		<div class="container-fluid">
			<!-- Header -->
			<!-- Header container -->
			<div class="container-fluid d-flex align-items-center">
				<div class="row">
					<div class="col-lg-10 col-md-10 mt-5 mb-8">
						<h1 class="display-2 text-white">Complexity Metrics</h1>
						<p class="text-white text-justify mb-3">
							There are three metrics relevant to inheritance include Lack of Cohesion in Methods (Hitz &
							Montazeri), Tight and Loose Class Cohesion.
						</p>
						<p class="text-justify text-white mb-1 ml-4">
							<span class="text-bold">Lack of Cohesion in Methods (Hitz & Montazeri)</span><span>, </span>
							which is so-called LCOM4, is counting the number of distinct components within a class. A component
							is a set of related methods that both access the same instance variable or have any direct or indirect
							invocation between them. There are lots of definitions of whether include the private classes or
							whether include the class variables. Currently, T4M uses all methods and variables to compute this
							metric.
						</p>
						<p class="text-white text-justify mb-3 ml-4">
							Detailed descriptions and influences:
							<a class="text-yellow text-link"
							   href="https://maisqual.squoring.com/wiki/index.php/Lack_of_Cohesion_in_Methods">
								Maisqual - Lack of Cohesion in Methods</a>,
							<a class="text-yellow text-link" href="http://www.arisa.se/compendium/node116.html">
								ARiSA - Lack of Cohesion in Methods</a>,
							<a class="text-yellow text-link"
							   href="https://www.aivosto.com/project/help/pm-oo-cohesion.html">
								Aivosto - Cohesion metrics</a>,
							<a class="text-yellow text-link"
							   href="https://objectscriptquality.com/docs/metrics/lack-cohesion-methods-lcom4">
								objectscriptquality.com - Lack of Cohesion in Methods (LCOM4)</a>,
							<a class="text-yellow text-link"
							   href="http://www.tusharma.in/technical/revisiting-lcom/">
								tusharma.in - Revisiting LCOM</a>.
						</p>
						
						<div class="collapse" id="collapse_description">
							<p class="text-justify text-white mb-1 ml-4">
								<span class="text-bold">Tight Class Cohesion & Loose Class Cohesion</span><span>, </span>
								which are the ratio of the connected method pairs to all possible method pairs. For TCC
								the connected method pairs are only direct connections. For LCC it contains both direct and
								indirect connections. A direct connection means that both methods access the same instance variable or
								they access the same instance variable through the invocation tree. An indirect connection means that
								they do not
								have a direct connection but being contained within the same component as mentioned in LCOM4.
							</p>
							<p class="text-white text-justify mb-3 ml-4">
								Detailed descriptions and influences:
								<a class="text-yellow text-link"
								   href="https://www.aivosto.com/project/help/pm-oo-cohesion.html">
									Aivosto - Cohesion metrics</a>.
							</p>
						</div>
						<a class="text-white text-link" data-toggle="collapse" href="#collapse_description"
						   role="button" aria-expanded="false" aria-controls="collapse_description">
							<i class="fas fa-sort"></i> Expand / Collapse
						</a>
					</div>
				</div>
			</div>
		</div>
	</div>
	
	<!-- Page content -->
	<div class="container-fluid mt--6" th:with="currentProjectInfo = ${projectList.get(projectList.size()-1)}">
		<!--图表-->
		<div class="row">
			<!-- chart -->
			<div class="col-xl-12">
				<div class="card">
					<div class="card-header bg-transparent">
						<div class="row align-items-center">
							<div class="col">
								<h6 class="text-muted text-uppercase ls-1 mb-1">Overview</h6>
								<h5 class="h3 mb-0">
									The distribution of classes on the Tight Class Cohesion and the Loose Class Cohesion, and the
									evolution during developing.
								</h5>
							</div>
						</div>
					</div>
					<div id="chart" class="card-body pb-4 pr-4 pl-0 pt-0 col-12" style="height:550px;"></div>
					<div class="modal fade" id="chart_data_model" tabindex="-1" role="dialog"
					     aria-labelledby="row_model_label" aria-hidden="true">
						<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable" role="document">
							<div class="modal-content">
								<div class="modal-header">
									<h5 class="modal-title">{{title}}</h5>
									<button type="button" class="close" data-dismiss="modal" aria-label="Close">
										<span aria-hidden="true">&times;</span>
									</button>
								</div>
								<div class="modal-body pt-0">
									<hr class="my-3">
									<div v-for="pointValue in pointValueList">
										<span>{{pointValue}}</span><br>
									</div>
									<hr class="my-3">
									<div v-for="pointDescription in pointDescriptionList">
										<div v-html="pointDescription"></div>
										<hr class="my-1">
									</div>
								</div>
								<div class="modal-footer">
									<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
								</div>
							</div>
						</div>
					</div>
					
					<script type="text/javascript" th:inline="javascript">
              let timeRecords = /*[[${timeRecords}]]*/;
              let classCohesionDataset = /*[[${classCohesionDataset}]]*/;

              function showTimelineChartForClass(type) {
                  let options = optionsFormatter(classCohesionDataset);
                  let option = {
                      options: options,
                      baseOption: {
                          timeline: {
                              axisType: 'category', loop: true, autoPlay: false, currentIndex: timeRecords.length - 1,
                              playInterval: 1000, symbol: 'circle', data: timeRecords,
                              controlStyle: {showNextBtn: true, showPrevBtn: true, showPlayBtn: true},
                              checkpointStyle: {animation: false},
                              label: {
                                  formatter: function (value, index) {
                                      return value.split(" ")[0];
                                  }
                              }
                          },
                          legend: {
                              data: ['Class', 'Enum', 'Annotation', 'Interface', 'Abstract Class', 'package-info'],
                              selected: {'package-info': false},
                          },
                          xAxis: {name: 'Tight Class Cohesion', type: 'value', nameLocation: 'center', nameGap: 22},
                          yAxis: {
                              name: 'Loose Class Cohesion', type: 'value',
                              nameLocation: 'center', nameRotate: 90, nameGap: 30
                          },
                          dataZoom: [
                              {type: 'inside', xAxisIndex: 0, start: 0, end: 100},
                              {type: 'inside', yAxisIndex: 0, start: 0, end: 100},
                          ],
                          tooltip: {
                              padding: 10, borderWidth: 1,
                              show: true, enterable: true, trigger: 'item', confine: true,
                              formatter: function (params) {
                                  if (params.componentType === "timeline") {
                                      return params.name;
                                  } else {
                                      let value = params.data
                                      return '<div style="border-bottom: 1px solid rgba(255,255,255,.3) ;padding-bottom: 7px;margin-bottom: 7px">'
                                          + 'Class: ' + value[3] + '<br>'
                                          + '<span style="font: 12px">'
                                          + 'Of Module: ' + value[4] + '</span>' + '<br>'
                                          + '</div>'
                                          + '<div style="border-bottom: 1px solid rgba(255,255,255,.3) ;padding-bottom: 7px;margin-bottom: 7px">'
                                          + 'Tight Class Cohesion: ' + value[0] + '<br>'
                                          + 'Loose Class Cohesion: ' + value[1] + '<br>'
                                          + 'Lack of Cohesion in Methods: ' + value[2] + '<br>'
                                          + '</div>'
                                          + 'Click the point to see all overlapped data.'
                                  }
                              }
                          },
                          grid: {bottom: 100}, color: ['#c23531'],
                          visualMap: [
                              {
                                  type: 'continuous', left: 'right', top: 'middle', calculable: true, precision: 0.1,
                                  text: ['Lack of Cohesion\nin Methods'], textGap: 30, dimension: 2, max: 10,
                                  inRange: {symbolSize: [10, 30]}, controller: {inRange: {color: ['#c23531']},},
                              },
                          ],
                      }
                  };
                  timelineChart.setOption(option);
              }

              function optionsFormatter(dataset) {
                  let options = [];
                  for (let i = 0; i < timeRecords.length; i++) {
                      options[i] = {
                          series: [{
                              name: 'Interface', type: 'scatter', animation: false,
                              data: dataset[timeRecords[i]]['Interface'],
                              itemStyle: {color: '#69bae5',},
                              emphasis: {
                                  label: {
                                      show: true, color: '#000000', position: 'top', fontWeight: 'normal',
                                      formatter: function (params) {
                                          return params.data[3];
                                      },
                                  },
                              },
                          }, {
                              name: 'Abstract Class', type: 'scatter', animation: false,
                              data: dataset[timeRecords[i]]['Abstract Class'],
                              itemStyle: {color: '#54ba3d',},
                              emphasis: {
                                  label: {
                                      show: true, color: '#000000', position: 'top', fontWeight: 'normal',
                                      formatter: function (params) {
                                          return params.data[3];
                                      },
                                  },
                              },
                          }, {
                              name: 'Class', type: 'scatter', animation: false,
                              data: dataset[timeRecords[i]]['Class'],
                              itemStyle: {color: '#c23531',},
                              emphasis: {
                                  label: {
                                      show: true, color: '#000000', position: 'top', fontWeight: 'normal',
                                      formatter: function (params) {
                                          return params.data[3];
                                      },
                                  },
                              },
                          }, {
                              name: 'Enum', type: 'scatter', animation: false,
                              data: dataset[timeRecords[i]]['Enum'],
                              itemStyle: {color: '#e7ad70',},
                              emphasis: {
                                  label: {
                                      show: true, color: '#000000', position: 'top', fontWeight: 'normal',
                                      formatter: function (params) {
                                          return params.data[3];
                                      },
                                  },
                              },
                          }, {
                              name: 'Annotation', type: 'scatter', animation: false,
                              data: dataset[timeRecords[i]]['Annotation'],
                              itemStyle: {color: '#e770e1',},
                              emphasis: {
                                  label: {
                                      show: true, color: '#000000', position: 'top', fontWeight: 'normal',
                                      formatter: function (params) {
                                          return params.data[3];
                                      },
                                  },
                              },
                          }, {
                              name: 'package-info', type: 'scatter', animation: false,
                              data: dataset[timeRecords[i]]['package-info'],
                              itemStyle: {color: '#171717',},
                              emphasis: {
                                  label: {
                                      show: true, color: '#000000', position: 'top', fontWeight: 'normal',
                                      formatter: function (params) {
                                          return params.data[3];
                                      },
                                  },
                              },
                          }]
                      }
                  }
                  return options;
              }

              // 基于准备好的dom，初始化echarts实例
              let timelineChart = echarts.init(document.getElementById('chart'));

              // 用于显示timeline chart的重叠数据
              let chart_data_vm = new Vue({
                  el: '#chart_data_model',
                  data: {
                      title: '',
                      pointValueList: [],
                      pointDescriptionList: [],
                  }
              })
              let currentTimelineIndex = timeRecords.length - 1;
              timelineChart.on('timelinechanged', function (params) {
                  currentTimelineIndex = params.currentIndex
              });
              timelineChart.on('click', 'series.scatter', function (param) {
                  chart_data_vm.title = timeRecords[currentTimelineIndex];

                  let pointValueList = [];
                  pointValueList.push("Tight Class Cohesion: " + param.value[0]);
                  pointValueList.push("Loose Class Cohesion: " + param.value[1]);
                  pointValueList.push("Lack of Cohesion of Methods: " + param.value[2]);
                  chart_data_vm.pointValueList = pointValueList;

                  let x = param.value[0];
                  let y = param.value[1];
                  let pointDescriptionList = [];
                  for (let key in classCohesionDataset) {
                      if (key === timeRecords[currentTimelineIndex]) {
                          for (let key2 of ['Interface', 'Abstract Class', 'Class', 'Enum', 'Annotation']) {
                              for (let row of classCohesionDataset[key][key2]) {
                                  if (row[0] === x && row[1] === y) {
                                      let text = '<span style="font: 12px">'
                                          + key2 + ': ' + row[3] + '</span>' + '<br>'
                                          + '<span style="font: 12px">'
                                          + 'Of module: ' + row[4] + '</span>' + '<br>';
                                      pointDescriptionList.push(text);
                                  }
                              }
                          }
                      }
                  }
                  chart_data_vm.pointDescriptionList = pointDescriptionList;
                  $('#chart_data_model').modal('show');
              });
					
					
					</script>
				</div>
				<!--表格-->
				<div class="row">
					<!-- 选择记录时间 -->
					<div class="col-xl-12">
						<div class="card-header border-0">
							<div class="row align-items-center">
								<div class="col-auto">
									<h6 class="text-uppercase text-muted ls-1 mb0">Details</h6>
									
									<!-- dropdown -->
									<div class="nav-item dropdown">
										<a class="nav-link pr-0" href="#" role="button" data-toggle="dropdown" aria-haspopup="true"
										   aria-expanded="false">
											<span class="h3 mb-0 mr-2" id="current_record_time"
											      th:text="${#dates.format(currentProjectInfo.getCreateDate(), 'yyyy-MM-dd HH:mm:ss')}"></span>
											<i class="fas fa-caret-down"></i>
										</a>
										<div class="dropdown-menu">
											<a th:class="${timeRecordStat.index} eq ${timeRecords.size()-1} ? 'dropdown-item active' : 'dropdown-item'"
											   href="javascript:void(0);"
											   th:each="timeRecord,timeRecordStat : ${timeRecords}"
											   th:onclick="selectRecordTime(this,[[${timeRecordStat.index}]])">
												<span th:text="${timeRecord}"></span>
											</a>
										</div>
									</div>
								
								</div>
							
							</div>
						</div>
					</div>
					<!--表格-->
					<div class="col-xl-12">
						<div class="card table-outline">
							<div class="table-responsive">
								<div id="toolbar">
									<div class="form-inline" role="form">
										<a id="previous-btn" class="btn btn-outline-primary btn-sm invisible"
										   href="javascript:void(0);"
										   onclick="showClassCohesion()">
											< previous</a>
									</div>
								</div>
								<table id="table"></table>
							</div>
						</div>
					</div>
					<!-- Modal -->
					<div class="modal fade" id="row_model" tabindex="-1" role="dialog"
					     aria-labelledby="row_model_label" aria-hidden="true">
						<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
							<div class="modal-content">
								<div class="modal-header">
									<h5 class="modal-title" id="row_model_label">
										<span class="h3 text-uppercase text-muted mr-2">{{ recordType }}</span>
										<span class="h3">{{ recordName }}</span>
									</h5>
									<button type="button" class="close" data-dismiss="modal" aria-label="Close">
										<span aria-hidden="true">&times;</span>
									</button>
								</div>
								<div class="modal-body">
									<div id="row_chart" class="card-body pb-4 pr-4 pl-0 pt-0 col-12" style="height:450px;"></div>
								</div>
								<div class="modal-footer">
									<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
								</div>
							</div>
						</div>
					
					</div>
				
				</div>
				<!--common modals-->
				<modal th:replace="fragments/common :: modals"></modal>
				<!-- Footer -->
				<footer th:replace="fragments/common :: footer"></footer>
			</div>
		</div>
		<script th:replace="fragments/common :: footer_imports"></script>
		<script>
        $(function () {
            // 首次渲染图表
            showTimelineChartForClass();
            // 首次渲染表格
            showClassCohesion();
        })

        let $table = $('#table')
        let projectRecordIndex = -1;

        // 选择记录
        function selectRecordTime(obj, index) {
            projectRecordIndex = index;
            let newTime = $(obj).text()
            $('#current_record_time').text(newTime)
            $(obj).siblings().removeClass("active")
            $(obj).addClass("active");
            showClassCohesion();
        }

        //表格：类级别的度量数据
        function showClassCohesion() {
            let columns = [{
                field: 'chart', title: ''
            }, {
                field: 'name', title: 'Class Name', sortable: true
            }, {
                field: 'lackOfCohesionInMethods4', title: 'Lack of Cohesion in Methods (LOCM4)', sortable: true
            }, {
                field: 'tightClassCohesion', title: 'Tight Class Cohesion', sortable: true
            }, {
                field: 'looseClassCohesion', title: 'Loose Class Cohesion', sortable: true
            }, {
                field: 'type', title: 'Class Type', sortable: true
            }, {
                field: 'declaration', title: 'Class Location', sortable: true
            }, {
                field: 'package', title: 'of Which Package', sortable: true
            }, {
                field: 'module', title: 'of Which Module', sortable: true
            }];
            $.get("/dashboard/cohesion/table/class?projectRecordIndex=" + projectRecordIndex).done(function (data) {
                let dataset = data;
                //给每行前面加上趋势图按钮（弹出模态框）
                for (i = 0; i < dataset.length; i++) {
                    dataset[i].chart =
                        '<a class="table-chart-icon" href="javascript:void(0);" data-toggle="modal" '
                        + 'data-target="#row_model" onclick="showTableChartForClass(\'' +
                        dataset[i].qualifiedName + '\')">'
                        + '<i class="fas fa-chart-bar"></i></a>'
                }
                buildTable($table, dataset, columns, 2, 0)
                $("#previous-btn").addClass("invisible");
            });
        }

        // 重新渲染表格
        function buildTable($el, dataset, columns, fixedNumber, fixedRightNumber) {
            $el.bootstrapTable('destroy').bootstrapTable({
                height: 500,
                columns: columns,
                data: dataset,
                toolbar: '#toolbar',
                search: true,
                showColumns: true,
                fixedColumns: true,
                fixedNumber: fixedNumber,
                fixedRightNumber: fixedRightNumber,
                // buttons: "",
                // buttonsPrefix: "",
                // buttonsClass: "btn btn-sm btn-outline-primary",
                showColumnsToggleAll: true,
                detailViewByClick: true,
                iconSize: "sm",
                showFullscreen: true,
            })
        }

        // 用于修改模块框title
        let inheritance_table_chart_title_vm = new Vue({
            el: '#row_model_label',
            data: {
                recordType: '',
                recordName: '',
            }
        })

        // 指定图表的配置项和数据
        let tableChartOptForClass = {
            grid: {
                bottom: '100'
            },
            // 声明一个 X 轴，类目轴（category）。默认情况下，类目轴对应到 dataset 第一列。
            xAxis: {
                type: 'category'
            },
            yAxis: [
                {
                    name: "Lack of Cohesion in Methods (LCOM4)",
                    type: 'value', nameLocation: 'center',
                    nameRotate: 90, nameGap: 30, interval: 1,
                }, {
                    name: "Tight / Loose Cohesion",
                    type: 'value', nameLocation: 'center',
                    nameRotate: 270, nameGap: 35, max: 1, min: 0,
                }
            ],
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    snap: true, type: "line",
                }
            },
            dataZoom: [
                {
                    type: 'slider', xAxisIndex: 0, start: 0, end: 100
                },
                {
                    type: 'inside', xAxisIndex: 0, start: 0, end: 100
                },
                {
                    type: 'slider', yAxisIndex: 0, left: 20, start: 0, end: 100
                },
                {
                    type: 'slider', yAxisIndex: 1, right: 20, start: 0, end: 100
                }
            ],
        };
        let inheritanceTableChart = echarts.init(document.getElementById('row_chart'));


        function showTableChartForClass(qualifiedName) {
            inheritance_table_chart_title_vm.recordName = qualifiedName;
            inheritance_table_chart_title_vm.recordType = "Class";
            // 异步加载数据
            inheritanceTableChart.clear();
            inheritanceTableChart.setOption(tableChartOptForClass);
            $.get("/dashboard/cohesion/table/chart/class?qualifiedName=" + qualifiedName).done(function (data) {
                // 填入数据
                inheritanceTableChart.setOption({
                    legend: {
                        data: ["Lack of Cohesion in Methods (LOCM4)", "Tight Class Cohesion", "Loose Class Cohesion"]
                    },
                    dataset: {
                        // 提供一份数据。
                        source: data
                    },
                    series: [
                        {
                            type: 'line', name: 'Lack of Cohesion in Methods (LOCM4)', yAxisIndex: 0,
                            lineStyle: {width: 3}, emphasis: {label: {show: true}},
                        },
                        {
                            type: 'line', name: 'Tight Class Cohesion', yAxisIndex: 1,
                            lineStyle: {width: 3, type: "dashed"}, emphasis: {label: {show: true}},
                        },
                        {
                            type: 'line', name: 'Loose Class Cohesion', yAxisIndex: 1,
                            lineStyle: {width: 3, type: "dashed"}, emphasis: {label: {show: true}},
                        },
                    ],
                });
            });
        }

        // 让echarts在模态框打开后重新渲染
        $('#row_model').on('shown.bs.modal', function () {
            inheritanceTableChart.resize()
        })
		
		
		</script>

</body>

</html>
